package cn.mini.mybatis.mybatis.executor.resultset;



import cn.mini.mybatis.mybatis.executor.Executor;
import cn.mini.mybatis.mybatis.mapping.BoundSql;
import cn.mini.mybatis.mybatis.mapping.MappedStatement;

import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;


/**
 * DefaultResultSetHandler 类实现了 ResultSetHandler 接口，
 * 用于处理数据库查询返回的结果集，将结果集转换为 Java 对象列表。
 */
public class DefaultResultSetHandler implements ResultSetHandler {

    /**
     * 绑定的 SQL 对象，包含了 SQL 语句、参数映射以及结果类型等信息。
     */
    private final BoundSql boundSql;

    /**
     * 构造函数，初始化 DefaultResultSetHandler 对象。
     *
     * @param executor 执行 SQL 语句的执行器，当前未使用。
     * @param mappedStatement 映射语句对象，封装了 SQL 语句的相关信息，当前未使用。
     * @param boundSql 绑定的 SQL 对象，包含 SQL 语句、参数映射以及结果类型等信息。
     */
    public DefaultResultSetHandler(Executor executor, MappedStatement mappedStatement, BoundSql boundSql) {
        this.boundSql = boundSql;
    }

    /**
     * 处理 Statement 执行后返回的结果集，将其转换为指定类型的对象列表。
     *
     * @param <E> 结果列表中元素的类型。
     * @param stmt 执行 SQL 查询的 Statement 对象。
     * @return 包含查询结果对象的列表。
     * @throws SQLException 当处理结果集过程中出现 SQL 相关异常时抛出。
     */
    @Override
    public <E> List<E> handleResultSets(Statement stmt) throws SQLException {
        // 获取 Statement 执行后返回的结果集
        ResultSet resultSet = stmt.getResultSet();
        try {
            // 调用 resultSet2Obj 方法将结果集转换为指定类型的对象列表
            return resultSet2Obj(resultSet, Class.forName(boundSql.getResultType()));
        } catch (ClassNotFoundException e) {
            // 若找不到结果类型对应的类，打印异常堆栈信息并返回 null
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 将结果集转换为指定类型的对象列表。
     *
     * @param <T> 结果列表中元素的类型。
     * @param resultSet 数据库查询返回的结果集。
     * @param clazz 要转换的目标对象的类。
     * @return 包含转换后对象的列表。
     */
    private <T> List<T> resultSet2Obj(ResultSet resultSet, Class<?> clazz) {
        // 用于存储转换后对象的列表
        List<T> list = new ArrayList<>();
        try {
            // 获取结果集的元数据，包含列数、列名等信息
            ResultSetMetaData metaData = resultSet.getMetaData();
            // 获取结果集的列数
            int columnCount = metaData.getColumnCount();
            // 遍历结果集中的每一行数据
            while (resultSet.next()) {
                // 通过反射创建目标类的实例
                T obj = (T) clazz.newInstance();
                // 遍历当前行的每一列数据
                for (int i = 1; i <= columnCount; i++) {
                    // 获取当前列的值
                    Object value = resultSet.getObject(i);
                    // 获取当前列的列名
                    String columnName = metaData.getColumnName(i);
                    if (columnName.contains("_")) {
                        columnName = columnName.replaceAll("_", "");
                    }
                    // 生成对应的 set 方法名
                    String setMethod = "set" + columnName.substring(0, 1).toUpperCase() + columnName.substring(1);
                    Method method;
                    // 如果列值是 Timestamp 类型，调用参数类型为 Date 的 set 方法
                    if (value instanceof Timestamp) {
                        method = clazz.getMethod(setMethod, Date.class);
                    } else {
                        // 否则，调用参数类型与列值类型相同的 set 方法
                        method = clazz.getMethod(setMethod, value.getClass());
                    }
                    // 通过反射调用对象的 set 方法，将列值设置到对象中
                    method.invoke(obj, value);
                }
                // 将处理好的对象添加到列表中
                list.add(obj);
            }
        } catch (Exception e) {
            // 若出现异常，打印异常堆栈信息
            e.printStackTrace();
        }
        return list;
    }
}